package com.example.repoprovider.tcc.impl;

import com.example.repoprovider.mapper.RepoMapper;
import com.example.repoprovider.mapper.TccTransactionMapper;
import com.example.repoprovider.model.Repo;
import com.example.repoprovider.model.TccTransaction;
import com.example.repoprovider.tcc.IRepoAction;
import io.seata.core.context.RootContext;
import io.seata.rm.tcc.api.BusinessActionContext;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.example.constants.TccState;
import org.example.exception.BizException;
import org.example.exception.ValidException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Date;

/**
 * @author Kong
 * @date 2021-9-14 16:48
 */
@Slf4j
@Service
public class RepoAction implements IRepoAction {
    @Autowired
    private RepoMapper repoMapper;
    @Autowired
    private TccTransactionMapper tccTransactionMapper;
    @Override
    public boolean prepareDecreaseRepo(BusinessActionContext businessActionContext, String productCode, Integer amount) {
        //1、判断传参
        if(StringUtils.isEmpty(productCode) || amount ==null || amount<0) {
            throw new ValidException("数据传输错误");
        }
        //2、查询账户，冻结库存
        Repo repo = repoMapper.getRepoForUpdate(productCode);
        if(repo.getCount() < amount) {
            throw new BizException("库存不足");
        }
        int row = repoMapper.updateRepoFrozen(productCode,amount);
        if(row < 0) {
            throw new BizException("冻结库存失败");
        }
        //3、插入日志
        // 避免出现悬挂问题
        TccTransaction tccTransaction = new TccTransaction();
        // 分支事务
        tccTransaction.setBranchId(String.valueOf(businessActionContext.getBranchId()));
        // 全局事务
        tccTransaction.setXid(RootContext.getXID());
        tccTransaction.setGmtCreate(new Date());
        tccTransaction.setGmtUpdate(new Date());
        // 状态
        tccTransaction.setState(TccState.PREPARED.getValue());
        tccTransactionMapper.insert(tccTransaction);
        return true;
    }

    @Override
    public boolean commitDecrease(BusinessActionContext businessActionContext) {
        String xid = businessActionContext.getXid();
        String branchId = String.valueOf(businessActionContext.getBranchId());
        String productCode = businessActionContext.getActionContext("productCode").toString();
        Integer count = Integer.valueOf(businessActionContext.getActionContext("amount").toString());
        // 查询TccTransaction，防止空提交和重复提交
        TccTransaction tccTransaction = tccTransactionMapper.getByXidAndBranchIdForUpdate(xid,branchId);
        if(tccTransaction == null) {
            throw new BizException("二阶段执行异常!一阶段未执行");
        } else if(tccTransaction.getState().equals(TccState.COMMITTED.getValue())) {
            // commit允许重复提交，但需要幂等
            // 如果状态为 TccState.COMMITTED，则说明已经提交过，直接返回true
            return true;
        } else if(TccState.ROLLBACKED.getValue().equals(tccTransaction.getState())) {
            throw new BizException("已经回滚的事务，不允许再次提交");
        }
        // 库存扣减
        int row = repoMapper.updateFrozenToUsed(productCode,count);
        if(row<1) {
            throw new BizException("库存扣减失败。xid="+xid);
        }
        tccTransactionMapper.updateStateByXidAndBranchId(TccState.COMMITTED.getValue(),xid,branchId);
        return true;
    }

    @Override
    public boolean rollbackDecrease(BusinessActionContext businessActionContext) {
        String xid = businessActionContext.getXid();
        String branchId = String.valueOf(businessActionContext.getBranchId());
        String productCode = businessActionContext.getActionContext("productCode").toString();
        Integer amount = Integer.valueOf(businessActionContext.getActionContext("amount").toString());
        // 查询日志
        TccTransaction tccTransaction = tccTransactionMapper.getByXidAndBranchIdForUpdate(xid,branchId);
        if(tccTransaction == null) {
            // 此时说明是空回滚，需要插入TccTransaction，避免try执行造成悬挂
            tccTransaction = new TccTransaction();
            // 分支事务
            tccTransaction.setBranchId(branchId);
            // 全局事务
            tccTransaction.setXid(xid);
            tccTransaction.setGmtCreate(new Date());
            tccTransaction.setGmtUpdate(new Date());
            // 状态
            tccTransaction.setState(TccState.ROLLBACKED.getValue());
            tccTransactionMapper.insert(tccTransaction);
            return true;
        } else if(TccState.ROLLBACKED.getValue().equals(tccTransaction.getState())) {
            // 说明已经回滚，满足幂等，直接返回true
            return true;
        } else if(TccState.COMMITTED.getValue().equals(tccTransaction.getState())) {
            // throw BizException
            log.warn("已经提交的事务不允许回滚，xid="+xid);
            return true;
        }
        // 执行到这，说明状态为TccState.Prepared
        // 执行回滚
        int row = repoMapper.rollbackFrozen(productCode,amount);
        if(row <1) {
            throw new BizException("全局事务回滚失败，回滚库存失败。xid=" + xid);
        }
        tccTransactionMapper.updateStateByXidAndBranchId(TccState.ROLLBACKED.getValue(),xid,branchId);
        return true;
    }
}
